//============================================================================================
/**
 * @file	pkthln_comm_pack.c
 * @brief	ポケスロン通信パッケージ
 * @author	Nozomu Saito
 */
//============================================================================================

#include "common.h"
#include "pkthln_comm_pack.h"

#define PKTHLN_COMM_PACK_DATABUFF_SIZE	( 38 )	// byte
#define PKTHLN_DUMMY_PACK_DATABUFF_SIZE	( PKTHLN_COMM_PACK_DATABUFF_SIZE )	// byte


//-------------------------------------
//	ダミーコールバックワーク
//=====================================
typedef struct {
	BOOL Init;
	int Command;
	u8 data[ PKTHLN_DUMMY_PACK_DATABUFF_SIZE ];
	int Size;
	u8 *HugeData;
}PKTHLN_COMMPACK_DUMMY_WORK;


//-------------------------------------
//	ダミーコールバックシステム
//=====================================
typedef struct {
	PKTHLN_COMMPACK_DUMMY_WORK	work[PKTHLN_COMM_PACK_DUMMY_WORK_NUM];	// ワーク
	const CommPacketTbl* pCommPacketLocal;	// コマンドテーブル
	int					 ListNum;			// コマンドテーブル数
	void*				 pCallBackWork;		// コールバックワーク
	TCB_PTR				 tcb;				// タスクワーク
}PKTHLN_COMMPACK_DUMMY_SYS;

//-------------------------------------
//	外部保持　システムワーク
//	comm_flgからpWorkの使用用途が変わる
//=====================================
typedef struct PKTHLN_COMMPACK_WORK_tag{
	u16 CommFlg;	// 通信パックフラグ
	u16 SizeBind;	//転送サイズ制約フラグ	0：なし　1：38バイト以上はアサート
	void *pWork;	// ワーク
}PKTHLN_COMMPACK_WORK;

static PKTHLN_COMMPACK_DUMMY_SYS *CommPack_DummySys_Init(
		const CommPacketTbl* pCommPacketLocal,const int inListNum, void* pWork, const int inHeapID );
static void CommPack_DummySys_Change( PKTHLN_COMMPACK_DUMMY_SYS* p_sys,
		const CommPacketTbl* pCommPacketLocal,const int inListNum, void* pWork);
static void CommPack_DummySys_Main( TCB_PTR tcb, void* p_work );
static void CommPack_DummySys_Delete( PKTHLN_COMMPACK_DUMMY_SYS* p_sys );
static BOOL CommPack_DummySys_SendData( PKTHLN_COMMPACK_DUMMY_SYS* p_sys,
										const int inCommand, const void *data, const int inSize );
static BOOL CommPack_DummySys_SendHugeData( PKTHLN_COMMPACK_DUMMY_SYS* p_sys,
										const int inCommand, const void *data, const int inSize );

//----------------------------------------------------------------------------
/**
 *	@brief	コマンドテーブル設定　パック関数
 *
 *	@param	pCommPacketLocal		コマンドテーブル
 *	@param	inListNum				リスト数
 *	@param	pWork					コールバックワーク
 *	@param	inCommFlg				通信パックフラグ
 *	@param	inHeapID				ヒープID
 *
 *	@retval	コマンドパックシステムワーク
 */
//-----------------------------------------------------------------------------
COMMPACK_PTR PKTHLN_CommPack_CommandInitialize(
							const CommPacketTbl* pCommPacketLocal,
							const int inListNum, void *pWork, const int inCommFlg, const int inHeapID )
{
	COMMPACK_PTR pack_ptr;

	// メモリ確保
	pack_ptr = sys_AllocMemory( inHeapID, sizeof(PKTHLN_COMMPACK_WORK) );
	pack_ptr->CommFlg = inCommFlg;
	pack_ptr->SizeBind = 0;
	pack_ptr->pWork = NULL;

	if( inCommFlg == PKTHLN_COMM_PACK_COMM ){
	    CommCommandInitialize( pCommPacketLocal, inListNum, pWork );
		OS_Printf( "通信モードセットアップ\n" );
	}else{
		pack_ptr->pWork = CommPack_DummySys_Init( pCommPacketLocal, inListNum, pWork, inHeapID );
		OS_Printf( "一人モードセットアップ\n" );
	}

	return pack_ptr;
}

//----------------------------------------------------------------------------
/**
 *	@brief	コマンドテーブル変更　パック関数
 *
 *  @param	pack_ptr				パックシステムポインタ
 *	@param	pCommPacketLocal		コマンドテーブル
 *	@param	inListNum				リスト数
 *	@param	pWork					コールバックワーク
 *
 *	@retval	コマンドパックシステムワーク
 */
//-----------------------------------------------------------------------------
void PKTHLN_CommPack_CommandChange( COMMPACK_PTR pack_ptr,
							const CommPacketTbl* pCommPacketLocal,
							const int inListNum, void *pWork)
{
	if( pack_ptr->CommFlg == PKTHLN_COMM_PACK_COMM ){
	    CommCommandInitialize( pCommPacketLocal, inListNum, pWork );
		OS_Printf( "通信モードコマンド変更\n" );
	}else{
		CommPack_DummySys_Change(pack_ptr->pWork, pCommPacketLocal, inListNum, pWork);
		OS_Printf( "一人モードコマンド変更\n" );
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	転送サイズ制約フラグのセット
 *	有効になると、以降の転送データサイズが38バイト以上でアサート
 *
 *  @param	pack_ptr				パックシステムポインタ
 *	@param	inFlg					0:制約ない	それ以外：38バイト制限
 *
 *	@retval	none
 */
//-----------------------------------------------------------------------------
void PKTHLN_CommPack_SetSizeBind( COMMPACK_PTR pack_ptr, const u8 inFlg )
{
	pack_ptr->SizeBind = inFlg;
}

//----------------------------------------------------------------------------
/**
 *	@brief	コマンド転送　パック関数
 *
 *	@param	inCommand			コマンド
 *	@param	data				データ
 *	@param	inSize				サイズ
 *	@param	pack_ptr			コマンドパックシステム
 *
 *	@retval	TRUE	成功
 *	@retval	FALSE	失敗
 */
//-----------------------------------------------------------------------------
BOOL PKTHLN_CommPack_CommSendData( const int inCommand, const void *data, const int inSize, COMMPACK_PTR pack_ptr  )
{
	if (pack_ptr->SizeBind){
		//38バイトチェック
		GF_ASSERT( inSize <= PKTHLN_COMM_PACK_DATABUFF_SIZE );
	}

	if( pack_ptr->CommFlg == PKTHLN_COMM_PACK_COMM ){
	    return CommSendData( inCommand, data, inSize );
	}else{
		return CommPack_DummySys_SendData( pack_ptr->pWork, inCommand, data, inSize );
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	コマンド転送　パック関数
 *
 *	@param	inCommand			コマンド
 *	@param	data				データ
 *	@param	inSize				サイズ
 *	@param	pack_ptr			コマンドパックシステム
 *
 *	@retval	TRUE	成功
 *	@retval	FALSE	失敗
 */
//-----------------------------------------------------------------------------
BOOL PKTHLN_CommPack_CommSendHugeData( const int inCommand, const void *data, const int inSize, COMMPACK_PTR pack_ptr  )
{
	if( pack_ptr->CommFlg == PKTHLN_COMM_PACK_COMM ){
	    return CommSendHugeData( inCommand, data, inSize );
	}else{
		return CommPack_DummySys_SendHugeData( pack_ptr->pWork, inCommand, data, inSize );
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	コマンドテーブル操作	パックシステム破棄
 *
 *	@param	pack_ptr	コマンドパックシステム
 */
//-----------------------------------------------------------------------------
void PKTHLN_CommPack_CommDelete( COMMPACK_PTR pack_ptr  )
{
	if( pack_ptr->CommFlg == PKTHLN_COMM_PACK_ONE ){
		CommPack_DummySys_Delete( pack_ptr->pWork );
	}
	sys_FreeMemoryEz( pack_ptr );
}

//----------------------------------------------------------------------------
/**
 *	@brief	自分の接続IDを取得
 *
 *	@param	pack_ptr	コマンドパックシステム
 *
 *	@return	接続ID
 */
//-----------------------------------------------------------------------------
int PKTHLN_CommPack_CommGetCurrentID( COMMPACK_PTR pack_ptr )
{
	if( pack_ptr->CommFlg == PKTHLN_COMM_PACK_COMM ){
	    return CommGetCurrentID();
	}else{
		return PKTHLN_COMM_PACK_DUMMY_NETID;
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	ダミーコールバックシステム
 *
 *	@param	pCommPacketLocal	コマンドテーブル
 *	@param	inListNum			テーブル数
 *	@param	pWork				ワーク
 *	@param	inHeapID			ヒープID
 *
 *	@return	作成したシステムワーク
 */
//-----------------------------------------------------------------------------
static PKTHLN_COMMPACK_DUMMY_SYS *CommPack_DummySys_Init(
		const CommPacketTbl* pCommPacketLocal,const int inListNum, void* pWork, const int inHeapID )
{
	PKTHLN_COMMPACK_DUMMY_SYS* p_sys;

	// メモリ確保
	p_sys = sys_AllocMemory( inHeapID, sizeof(PKTHLN_COMMPACK_DUMMY_SYS) );
	memset( p_sys, 0, sizeof(PKTHLN_COMMPACK_DUMMY_SYS) );

	p_sys->pCommPacketLocal	= pCommPacketLocal;
	p_sys->ListNum = inListNum;
	p_sys->pCallBackWork	= pWork;

	p_sys->tcb = VWaitTCB_Add( CommPack_DummySys_Main, p_sys, PKTHLN_COMM_PACK_DUMMY_TCB_PRI );

	return p_sys;
}

//----------------------------------------------------------------------------
/**
 *	@brief	ダミーコールバックシステム
 *
 *  @param	p_sys				ダミーシステムポインタ
 *	@param	pCommPacketLocal	コマンドテーブル
 *	@param	inListNum			テーブル数
 *	@param	pWork				ワーク
 *
 *	@return	none
 */
//-----------------------------------------------------------------------------
static void CommPack_DummySys_Change( PKTHLN_COMMPACK_DUMMY_SYS* p_sys,
		const CommPacketTbl* pCommPacketLocal,const int inListNum, void* pWork)
{
	p_sys->pCommPacketLocal	= pCommPacketLocal;
	p_sys->ListNum = inListNum;
	p_sys->pCallBackWork	= pWork;
}

//----------------------------------------------------------------------------
/**
 *	@brief	メインTCB
 *
 *	@param	tcb			TCBポインタ
 *	@param	p_work		ワークポインタ
 */
//-----------------------------------------------------------------------------
static void CommPack_DummySys_Main( TCB_PTR tcb, void* p_work )
{
	PKTHLN_COMMPACK_DUMMY_SYS* p_sys = p_work;
	int i;

	// コールバック処理
	for( i=0; i<PKTHLN_COMM_PACK_DUMMY_WORK_NUM; i++ ){

		// データがあるかチェック
		if( p_sys->work[i].Init == TRUE ){
			//サイズチェックしてサイズがバッファサイズより多い場合は、ヒュージデータとしてコールバック
			if (p_sys->work[i].Size <= PKTHLN_DUMMY_PACK_DATABUFF_SIZE){
				p_sys->pCommPacketLocal[ p_sys->work[i].Command ].callbackFunc(
					PKTHLN_COMM_PACK_DUMMY_NETID, p_sys->work[i].Size,
					p_sys->work[i].data, p_sys->pCallBackWork );
			}else{
				p_sys->pCommPacketLocal[ p_sys->work[i].Command ].callbackFunc(
					PKTHLN_COMM_PACK_DUMMY_NETID, p_sys->work[i].Size,
					p_sys->work[i].HugeData, p_sys->pCallBackWork );
			}
			// ワーククリア
			memset( &p_sys->work[i], 0, sizeof(PKTHLN_COMMPACK_DUMMY_WORK) );
		}
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	ダミーコールバックシステム破棄
 *
 *	@param	p_sys	ダミーシステムポインタ
 */
//-----------------------------------------------------------------------------
static void CommPack_DummySys_Delete( PKTHLN_COMMPACK_DUMMY_SYS* p_sys )
{
	TCB_Delete( p_sys->tcb );
	sys_FreeMemoryEz( p_sys );
}

//----------------------------------------------------------------------------
/**
 *	@brief	コマンド登録
 *
 *	@param	p_sys		システム
 *	@param	inCommand		コマンド
 *	@param	data		データ
 *	@param	inSize		サイズ
 *
 *	@retval	TRUE	成功
 *	@retval	FALSE	失敗
 */
//-----------------------------------------------------------------------------
static BOOL CommPack_DummySys_SendData( PKTHLN_COMMPACK_DUMMY_SYS* p_sys,
										const int inCommand, const void *data, const int inSize )
{
	int i;
	int check_size;
	int command;
	PKTHLN_COMMPACK_DUMMY_WORK* p_work;

	// 空いているワーク取得
	p_work = NULL;
	for( i=0; i<PKTHLN_COMM_PACK_DUMMY_WORK_NUM; i++ ){
		if( p_sys->work[i].Init == FALSE ){
			p_work = &p_sys->work[i];
			break;
		}
	}
	// 登録数オーバー
	GF_ASSERT( p_work );

	command = inCommand;
	// コマンドの共通部分数分引く
	command -= CS_COMMAND_MAX;

	// 登録するコマンドのサイズとデータサイズが等しいかチェック
	if (p_sys->pCommPacketLocal[ command ].getSizeFunc != NULL){
		check_size = p_sys->pCommPacketLocal[ command ].getSizeFunc();
		GF_ASSERT( check_size == inSize );
	}
	// ダミーバッファサイズより小さいかチェック
	GF_ASSERT( inSize <= PKTHLN_DUMMY_PACK_DATABUFF_SIZE );

	// データ設定
	p_work->Command = command;
	if( inSize > 0 ){	// サイズがあったらコピー
		memcpy( p_work->data, data, inSize );
	}
	p_work->Size	= inSize;
	p_work->Init	= TRUE;
	return TRUE;
}

//----------------------------------------------------------------------------
/**
 *	@brief	コマンド登録
 *
 *	@param	p_sys		システム
 *	@param	inCommand		コマンド
 *	@param	data		データ
 *	@param	inSize		サイズ
 *
 *	@retval	TRUE	成功
 *	@retval	FALSE	失敗
 */
//-----------------------------------------------------------------------------
static BOOL CommPack_DummySys_SendHugeData( PKTHLN_COMMPACK_DUMMY_SYS* p_sys,
										const int inCommand, const void *data, const int inSize )
{
	int i;
	int check_size;
	int command;
	PKTHLN_COMMPACK_DUMMY_WORK* p_work;

	// 空いているワーク取得
	p_work = NULL;
	for( i=0; i<PKTHLN_COMM_PACK_DUMMY_WORK_NUM; i++ ){
		if( p_sys->work[i].Init == FALSE ){
			p_work = &p_sys->work[i];
			break;
		}
	}
	// 登録数オーバー
	GF_ASSERT( p_work );

	command = inCommand;
	// コマンドの共通部分数分引く
	command -= CS_COMMAND_MAX;

	// 登録するコマンドのサイズとデータサイズが等しいかチェック
	check_size = p_sys->pCommPacketLocal[ command ].getSizeFunc();
	//可変長サイズでないならばチェック
	if (check_size != COMM_VARIABLE_SIZE){
		GF_ASSERT( check_size == inSize );
	}

	//バッファ関数からバッファを取得

	if (p_sys->pCommPacketLocal[ command ].getAddrFunc!=NULL){
		void * buff;
		buff = (void *)p_sys->pCommPacketLocal[ command ].getAddrFunc(
				PKTHLN_COMM_PACK_DUMMY_NETID, p_sys->pCallBackWork, inSize);
		if( inSize > 0 ){	// サイズがあったらコピー
			memcpy( buff, data, inSize );
		}
		//データポインタを代入
		p_work->HugeData = (u8 *)buff;
	}else{
		//データポインタを代入
		p_work->HugeData = (u8 *)data;
	}

	// データ設定
	p_work->Command = command;

	p_work->Size	= inSize;
	p_work->Init	= TRUE;
	return TRUE;
}

#if 0
//----------------------------------------------------------------------------
/**
 *	@brief	通信子データの先頭アドレスを返す
 *
 *	@param	通信データ
 *
 *	@retval	void *		先頭アドレス
 */
//-----------------------------------------------------------------------------
void *PKTHLN_CommPack_GetChildData(PTHLN_COMM_DATA *pData)
{
	return pData->Data;
}

//----------------------------------------------------------------------------
/**
 *	@brief	通信子データの先頭アドレスを返す
 *
 *	@param	通信データ
 *
 *	@retval	void *		先頭アドレス
 */
//-----------------------------------------------------------------------------
void *PKTHLN_CommPack_GetParentData(PTHLN_COMM_DATA *pData)
{
	u8 size = pData->Cds;
	return &pData->Data[size];
}
#endif

//----------------------------------------------------------------------------
/**
 *	@brief	データ取得
 *
 *	@param	*pData		ポケスロン通信データポインタ
 *
 *	@return	実データ先頭アドレス
 */
//-----------------------------------------------------------------------------
void *PKTHLN_CommPack_GetData(PTHLN_COMM_DATA *pData)
{
	return pData->Data;
}